﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq.Expressions;
using System.Windows.Controls;
using System.Windows.Input;
using Dispatcher.Helpers;

namespace Dispatcher.ViewModels.Base
{
    /// <summary>
    /// View Model Base.
    /// </summary>
    public abstract class ViewModelBase : IViewModelBase, INotifyPropertyChanged
    {
        /// <summary>
        /// Property values
        /// </summary>
        private readonly Dictionary<string, object> _values = new Dictionary<string, object>();

        /// <summary>
        /// Initializes a new instance of the <see cref="ViewModelBase"/> class.
        /// </summary>
        protected ViewModelBase()
        {
            CommandBindings = new List<CommandBinding>();
        }

        /// <summary>
        /// Occurs when [refresh data event].
        /// </summary>
        public static event EventHandler RefreshDataEvent;

        /// <summary>
        /// Occurs when a property value changes.
        /// </summary>
        public event PropertyChangedEventHandler PropertyChanged;

        /// <summary>
        /// Gets or sets the command bindings.
        /// </summary>
        /// <value>
        /// The command bindings.
        /// </value>
        public List<CommandBinding> CommandBindings { get; set; }

        /// <summary>
        /// Gets or sets the command executed.
        /// </summary>
        public EventHandler<ExtendedEventArgs> CommandExecuted { get; set; }

        /// <summary>
        /// Binds the specified model.
        /// </summary>
        /// <param name="model">The model.</param>
        /// <param name="view">The view.</param>
        public static void Bind(ViewModelBase model, Control view)
        {
            view.DataContext = model;
            view.CommandBindings.AddRange(model.CommandBindings);
        }

        /// <summary>
        /// Rises the refresh data event.
        /// </summary>
        /// <param name="o">The o.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        public static void RiseRefreshDataEvent(object o, EventArgs e)
        {
            if (RefreshDataEvent != null)
            {
                RefreshDataEvent(o, e);
            }
        }

        /// <summary>
        /// Binds the specified view.
        /// </summary>
        /// <param name="view">The view.</param>
        public void BindToView(Control view)
        {
            view.DataContext = this;
            view.CommandBindings.AddRange(CommandBindings);
        }

        /// <summary>
        /// Gets the specified expression.
        /// </summary>
        /// <typeparam name="T">Property type</typeparam>
        /// <param name="expression">The expression.</param>
        /// <returns>Property value</returns>
        public T Get<T>(Expression<Func<T>> expression)
        {
            var propertyName = PropertyNameProvider.GetPropertyName(expression);

            object value;
            if (_values.TryGetValue(propertyName, out value))
            {
                return (T)value;
            }

            return default(T);
        }

        /// <summary>
        /// Sets the specified expression.
        /// </summary>
        /// <typeparam name="T">Property type</typeparam>
        /// <param name="expression">The expression.</param>
        /// <param name="value">The value.</param>
        public void Set<T>(Expression<Func<T>> expression, T value)
        {
            var propertyName = PropertyNameProvider.GetPropertyName(expression);

            if (_values.ContainsKey(propertyName))
            {
                _values[propertyName] = value;
            }
            else
            {
                _values.Add(propertyName, value);
            }

            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }
    }
}
